import math
from math import *

import pyglet
from pyglet.window import key, mouse
from pyglet.gl import *
from pyglet import gl

import cocos
from cocos.actions import *
from cocos.layer import *
from cocos.scene import *
from cocos.sprite import *
from cocos.director import director
from cocos.scenes.transitions import *
from cocos.euclid import Point2
from cocos.draw import *
from cocos.text import *

import Box2D as box2d

from globalvars import *
from res import Images
from glprimitives import *
import entity


###--------------------------###       
#        
#    Turtle
#   - the player, has targetMarker
#   - and turtlePen
#
###---------------------------### 

class Turtle(entity.Entity):
    def __init__(self, world, image=Images.turtle,*args, **kwargs):
        "Init and keys"
        entity.Entity.__init__(self, world, image, *args, **kwargs)
        self.fsm = entity.FSM(self)
        self.fsm.changeState(self.StillAlive(self))
        self.keys = key.KeyStateHandler()
        director.window.push_handlers(self.keys)
        
        
        "Other data"
        self.hp = 100
        self.targetPt = box2d.b2Vec2(0/RATIO,0/RATIO)
        self.maxSpeed = 25
        self.lastPos = box2d.b2Vec2(0,0)
        self.targetMarker = TargetMarker(world,turtle=self)
        self.turtlePen = TurtlePen(world, turtle= self)
        self.penDown = True
        
        "BodyDef"
        self.bodyDef = box2d.b2BodyDef()
        self.bodyDef.linearDamping = 0.5
        self.bodyDef.angularDamping = 0.5
        self.bodyDef.allowSleep = True
        self.bodyDef.isSleeping = True        
        "Box ShapeDef"
        self.shellShapeDef = box2d.b2PolygonDef()
        self.shellShapeDef.SetAsBox(
                          self.width/RATIO/2.0, 
                         self.height/RATIO/2.0, 
                         (0,0), 0)
        self.shellShapeDef.density = 1.0
        self.shellShapeDef.friction = 0.3
        self.shellShapeDef.restitution = 0.6
        self.shellShapeDef.SetUserData(self)
        
        "Create the body and the shape(s)"
        self.body = self.world.CreateBody(self.bodyDef)
        self.body.SetBullet(True)
        self.shellShape = self.body.CreateShape(self.shellShapeDef)
        self.body.SetMassFromShapes()
        
        
        #pyglet.clock.schedule_interval(self.recordLastPos,1/20.)
        pyglet.clock.schedule_interval(self.drawLine,1/15.)
    
    def drawLine(self,dt):
        if self.penDown:
            if self.body.linearVelocity.tuple() != (0,0):
                self.turtlePen.addLineSegment(dt)
                self.lastPos = box2d.b2Vec2(*self.body.position.tuple())
                #print "recordLastPos",
    
                
    def update(self,dt):
        self.fsm.update(dt)
        
    def on_key_press(self, symbol, modifiers):
        if symbol == key.SPACE:
            self.togglePen()
    
    def on_mouse_press(self, x, y, button, modifiers):
        self.targetPt = box2d.b2Vec2(x/RATIO,y/RATIO)
        self.targetMarker.setTarget(self.targetPt)
        
    def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
        self.targetPt = box2d.b2Vec2(x/RATIO,y/RATIO)
        self.targetMarker.setTarget(self.targetPt)
    
    def move(self,dt):
        if self.body.position.tuple() != self.targetPt.tuple():
            vel = self.arrive(dt)
            angle = self.getAngleTo(self.targetPt)#self.arriveRotate(dt)
            #print degrees(self.arriveRotate(dt))
            self.body.angle = angle #- pi/2
            self.body.ApplyImpulse(vel, self.body.position)
            if (self.targetPt - self.body.position).Length() < 0.01:
                self.body.position = self.targetPt
                self.body.linearVelocity = (0,0)
                self.body.angularVelocity = 0
                return
    
    def togglePen(self):
        if self.penDown == True:
            self.penDown = False
        elif self.penDown == False:
            self.lastPos = box2d.b2Vec2(*self.body.position.tuple())
            self.penDown = True
        
    
    def seek(self,dt):
        DesiredVelocity = (self.targetPt - self.body.position)
        DesiredVelocity.Normalize()
        DesiredVelocity *= self.maxSpeed
        return (DesiredVelocity - self.body.linearVelocity)

    
    def arrive(self,dt=0,deceleration=1):
        ToTarget = self.targetPt - self.body.position
        dist =  ToTarget.Length()
        if (dist > 0):
            DecelerationTweaker = self.maxSpeed;
            speed = dist / deceleration * DecelerationTweaker
            speed = min(speed, self.maxSpeed)
            DesiredVelocity = ToTarget * speed / dist;
            CurrentVelocity = self.body.linearVelocity
            return DesiredVelocity - CurrentVelocity
        return box2d.b2Vec2(0,0)
    
    def getAngleTo(self,other):
        xs = other.x - self.body.position.x
        ys = other.y - self.body.position.y
        theta = atan2(ys,xs)
        if theta < 0:
            theta += pi*2
        return theta  
        
           
    class StillAlive(entity.State):
        "I'm alive!"
        def Enter(self,dt):
            pass
        
        def Execute(self,dt):
            "Things a PlayerBox do when still alive"
            self.ent.move(dt)
            
            #~ self.ent.drawLine(dt)

            "Update the sprite pos and rot based on its b2Body"
            self.ent.position = (self.ent.body.position * RATIO).tuple()
            self.ent.rotation = -(degrees(self.ent.body.angle))
        
        def Exit(self,dt):
            pass

###--------------------------###       
#        
#    TurtlePen
#   - line drawn
#
###---------------------------### 

class TurtlePen(Layer):
    def __init__(self, world, turtle=None,*args, **kwargs):
        "Init and keys"
        Layer.__init__(self)
        
        self.id = entity.Entity.nextValidID 
        entity.Entity.nextValidID += 1
        self.world = world
        self.turtle = turtle
        self.fsm = entity.FSM(self)
        self.inkColor = (20,20,20,230)
        self.inkLeft = 160
        self.inkWidth = 6/RATIO/2
        
        self.addedLine = False
        
        self.penLine = self.PenLine(pen=self, color = (0,0,0,200))
        self.add(self.penLine)
        
        "Other attr"
        self.lineShapes = []
        self.currentLinePos = box2d.b2Vec2(0,0)
        
        "BodyDef"
        self.bodyDef = box2d.b2BodyDef()
        
        "Create the body and the shape(s)"
        self.body = self.world.CreateBody(self.bodyDef)
        self.fsm.changeState(self.Drawn(self))
        
    def addLineSegment(self,dt):
        start = box2d.b2Vec2(*self.turtle.lastPos.tuple())
        end   = box2d.b2Vec2(*self.turtle.body.position.tuple())
        vec = start - end
        vecnorm = box2d.b2Vec2(*vec.tuple()); vecnorm.Normalize()
        dist = vec.Length()

        if self.inkLeft <= 0:
            return
        
        if self.inkLeft < dist:
            x = box2d.b2Vec2(*vec.tuple())
            x.Normalize()
            x *= self.inkLeft
            end = start + x
            vec = start - end
            vecnorm = box2d.b2Vec2(*vec.tuple()); vecnorm.Normalize()
            self.inkLeft = 0
        else:
            self.inkLeft -= dist
        
        #~ print self.inkLeft, dist, self.inkLeft < dist      
        leftvert = self.rotateVec(vecnorm, degrees(90)) *self.inkWidth
        rightvert = self.rotateVec(vecnorm, degrees(-90)) *self.inkWidth
        
        sd = box2d.b2PolygonDef()

        sd.vertexCount = 4
        sd.setVertex(0,start.x+leftvert.x,start.y+leftvert.y)
        sd.setVertex(1,start.x+rightvert.x,start.y+rightvert.y)
        sd.setVertex(3,end.x+leftvert.x,end.y+leftvert.y)
        sd.setVertex(2,end.x+rightvert.x,end.y+rightvert.y)
        sd.isSensor = True

        seg = self.body.CreateShape(sd)
        seg.SetUserData(sd)
        
        currentVerts = seg.GetUserData().getVertices_b2Vec2()
        
        vert1,vert2 = (currentVerts[0]*RATIO).tuple(), (currentVerts [1]*RATIO).tuple()
        vert3,vert4 = (currentVerts[2]*RATIO).tuple(), (currentVerts [3]*RATIO).tuple()
        
        self.lineShapes.append(seg)
        
        self.lastPt = box2d.b2Vec2(self.turtle.body.position.x, self.turtle.body.position.y)
        self.lastVel = box2d.b2Vec2(self.turtle.body.linearVelocity.x, self.turtle.body.linearVelocity.y)
        #self.addedLine = True
        

    def rotateVec(self,vec,angle=0): # angle in radians
        newVec = box2d.b2Vec2(0,0)
        newVec.x = vec.x * cos(angle) - vec.y * sin(angle)
        newVec.y = vec.x * sin(angle) + vec.y * cos(angle)
        return newVec
        
    def update(self,dt):
        self.fsm.update(dt)

    class Drawn(entity.State):
        def Enter(self,dt):
            pass
            
        def Execute(self,dt):
            pass

    class PenLine(cocos.cocosnode.CocosNode):
        def __init__(self, pen= None, color = (0,0,0,255)):
            cocos.cocosnode.CocosNode.__init__(self)
            self.pen = pen
            self.color = color
            self.turtle = self.pen.turtle
        
#        def draw(self):
#            glPushMatrix()
#            self.transform()
#            glColor4ub(*self.color)
#            glLineWidth(1)
#            glBegin(GL_QUADS)
#            for seg in self.pen.lineShapes:
#                verts = [(v*RATIO).tuple() for v in seg.GetUserData().getVertices_b2Vec2()]
#                for v in verts:
#                    glVertex2f(*v)
#            glEnd()
#            glPopMatrix()
        
        def draw(self):
            if self.pen.lineShapes == []:
                return
            glPushMatrix()
            self.transform()
            
            glColor4ub(*self.color)
            glLineWidth(1)
            
            glBegin(GL_TRIANGLE_STRIP)
            verts = []
             
            firstVerts = self.pen.lineShapes[0].GetUserData().getVertices_b2Vec2()
            verts.append((firstVerts[0]*RATIO).tuple())
            verts.append((firstVerts[1]*RATIO).tuple())
            
            for seg in self.pen.lineShapes:
                segVerts = seg.GetUserData().getVertices_b2Vec2()
                verts.append((segVerts[2]*RATIO).tuple())
                verts.append((segVerts[3]*RATIO).tuple())
                
            for v in verts:
                glVertex2f(*v)
                
            glEnd()
            glPopMatrix()


###--------------------------###       
#        
#    TargetMarker
#   - Turtle's marker
#
###---------------------------### 

class TargetMarker(entity.Entity):
    def __init__(self, world, turtle=None, image=Images.target_marker, *args, **kwargs):
        "Init and keys"
        entity.Entity.__init__(self, world, image, *args, **kwargs)
        self.turtle = turtle
        self.fsm = entity.FSM(self)
        self.scale = 0.6
        self.color = (255,0,0)
        self.fadingIn = False
        self.fadingOut = False
        
        "BodyDef"
        self.bodyDef = box2d.b2BodyDef()
        #self.bodyDef.angle = degrees(-90)
        self.bodyDef.linearDamping = 0.5
        self.bodyDef.angularDamping = 0.5
        
        
        "Box ShapeDef"
        self.targetShapeDef = box2d.b2PolygonDef()
        self.targetShapeDef.SetAsBox(
                          self.width/RATIO/2.0, 
                         self.height/RATIO/2.0, 
                         (0,0), 0)
        self.targetShapeDef.SetUserData(self)
        self.targetShapeDef.isSensor = True
        
        "Create the body and the shape(s)"
        self.body = self.world.CreateBody(self.bodyDef)
        self.targetShape = self.body.CreateShape(self.targetShapeDef)
        
        self.fsm.changeState(self.WaitingTurtle(self))
    
    def setTarget(self,pos):
        self.body.position = pos
        self.do(ScaleTo( 1, 0.05 )+ ScaleTo( 0.6, 0.05 ))
        #self.fsm.changeState(self.NewTargetSet(self))
    
    def update(self,dt):
        self.fsm.update(dt)
        #print self.fsm.currentState
        
    class WaitingTurtle(entity.State):
        def Enter(self,dt):
            self.ent.opacity = 140
            
        def Execute(self,dt):
            self.ent.position = (self.ent.body.position * RATIO).tuple()
            self.ent.rotation = -(degrees(self.ent.body.angle))

